package com.cxyjy.cfyuiapi.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.cxyjy.cfyuiapi.service.UploadImageService;
import com.cxyjy.common.BusinessException;
import com.cxyjy.common.util.FileNameGenerator;
import com.cxyjy.common.util.JFileUtil;
import com.cxyjy.common.util.JObjectUtil;
import com.cxyjy.common.util.ResultUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import static com.cxyjy.common.constants.CfyuiConstants.CFYUI_EXPECTED_IMAGE_EXTENSIONS;
import static com.cxyjy.common.constants.CfyuiConstants.UPLOAD_IMAGE_URL;
import static com.cxyjy.common.util.JFileUtil.*;
import static com.cxyjy.common.util.JObjectUtil.*;
import static com.cxyjy.common.util.ResultCode.*;

@Slf4j
@Service("uploadImageService")
public class UploadImageServiceImpl implements UploadImageService {

    /**
     * 通过图片文件上传单张图片到comfyui
     *
     * @param multipartFile
     * @return
     * @throws IOException
     */
    @Override
    public String uploadFaceSwapImage(MultipartFile multipartFile) throws IOException {
        //获取文件名
        String fileName = multipartFile.getOriginalFilename();
        String extension = "";
        String fileNameWithoutExtension = "";
        if (fileName != null && fileName.contains(".")) {
            // 返回最后一个 "." 之后的部分作为后缀名
            extension = fileName.substring(fileName.lastIndexOf(".") + 1);
            fileNameWithoutExtension = fileName.substring(0, fileName.lastIndexOf("."));
        } else {
            return "10099";
        }
        // 创建临时目录
        Path tempDir = Files.createTempDirectory("faceSwapTemp_");
        String tempImageFileName = fileNameWithoutExtension + "." + extension;
        String safeName = new String(tempImageFileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8);

        File tempImageFile = new File(tempDir.toFile(), safeName);
        boolean fileCreated = tempImageFile.createNewFile();
        if (fileCreated) {
            log.info("A user just uploaded an image named " + tempImageFile.getName());
        }
        //转换为File,方便之后判断是不是人脸
        try {
            multipartFile.transferTo(tempImageFile);
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
        uploadImageToComfyUI(UPLOAD_IMAGE_URL, tempImageFile);
        if (!deleteDirectory(tempImageFile)) {
            log.warn("临时目录删除失败: {}", tempDir);
        }
        return fileName;
    }

    /**
     * 通过图片url上传单张图片到comfyui
     *
     * @param urlStr
     * @return 图片文件名
     * @throws IOException
     */
    @Override
    public String uploadFaceSwapImage(String urlStr) throws IOException {
        //通过FileNameGenerator的getNextFileNameIndex计数器方法，让文件名后缀在1～x之间变化
//        String nextFileNameIndexStr = FileNameGenerator.getInstance().getNextFileNameIndex();

        String fileName = stringValue(new StringBuffer()
                .append("templateImage")
                .append(createId())//随机数ID，comfyui只认文件名，如果文件名重复会出不换脸的图！！
                .append(JFileUtil.getImageFormat(urlStr)));

        // 创建随机临时目录
        Path tempDir = Files.createTempDirectory("faceSwapTemp_");
        File tempImageFile = new File(tempDir.toFile(), fileName); // 使用原始文件名

        // 下载文件
        try (InputStream in = new URL(urlStr).openStream()) {
            Files.copy(in, tempImageFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
            log.info("A user just uploaded an image named " + fileName);
        } catch (Exception e) {
            throw new BusinessException(URL_DOWNLOAD_ERROR);
        }

        // 后续处理
        uploadImageToComfyUI(UPLOAD_IMAGE_URL, tempImageFile);
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("imageFileName", fileName);
        if (!deleteDirectory(tempImageFile)) {
            log.warn("临时目录删除失败: {}", tempDir);
        }
        return fileName;
    }

    /**
     * 通过url上传图片换脸所需的目标图片和模板图片到comfyui
     * 将目标图片(targetImage)中的人脸换为模板图片(sourceImage)中的人脸
     *
     * @param targetImageUrlStr
     * @param sourceImageUrlStr
     * @return 带有目标图片和模板图片文件名的Map
     * @throws IOException
     */
    @Override
    public Map<String, Object> uploadImageFaceSwapImage(String targetImageUrlStr, String sourceImageUrlStr) throws IOException {
        Map<String, Object> resultMap = new HashMap<>();
        String nextFileNameIndexStr = FileNameGenerator.getInstance().getNextFileNameIndex();

        if (!checkFileExtension(targetImageUrlStr, CFYUI_EXPECTED_IMAGE_EXTENSIONS) ||
                !checkFileExtension(sourceImageUrlStr, CFYUI_EXPECTED_IMAGE_EXTENSIONS)) {
            resultMap.put("error", 10000);
        }

        //拼接目标图片和模板图片的文件名，例如:targetImage7.jpg
        String targetImageFileName = stringValue(new StringBuffer()
                .append("targetImage")
                .append(nextFileNameIndexStr)
                .append(JFileUtil.getImageFormat(targetImageUrlStr)));

        String sourceImageFileName = stringValue(new StringBuffer()
                .append("sourceImage")
                .append(nextFileNameIndexStr)
                .append(JFileUtil.getImageFormat(sourceImageUrlStr)));

        // 创建随机临时目录
        Path tempDir = Files.createTempDirectory("faceSwapTemp_");
        File targetImageFile = new File(tempDir.toFile(), targetImageFileName); // 使用原始文件名
        File sourceImageFile = new File(tempDir.toFile(), sourceImageFileName); // 使用原始文件名

        // 下载文件
        try (InputStream targetImageIs = new URL(targetImageUrlStr).openStream();
             InputStream sourceImageIs = new URL(sourceImageUrlStr).openStream()) {
            Files.copy(targetImageIs, targetImageFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
            Files.copy(sourceImageIs, sourceImageFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
            uploadImageToComfyUI(UPLOAD_IMAGE_URL, targetImageFile);
            uploadImageToComfyUI(UPLOAD_IMAGE_URL, sourceImageFile);
            log.info("A user just uploaded a target image named " + targetImageFile.getName());
            log.info("A user just uploaded a target image named " + sourceImageFile.getName());
            resultMap.put("targetImageFileName", targetImageFileName);
            resultMap.put("sourceImageFileName", sourceImageFileName);
            return resultMap;
        } catch (Exception e) {
            return null;
        } finally {
            deleteDirectory(tempDir);
        }
    }

    /**
     * 通过图片文件或者url上传图片换脸所需图片
     * @param image
     * @param imageUrlStr
     * @return
     */
    @Override
    public String uploadImageFaceSwapImageHybrid(MultipartFile image, String imageUrlStr) throws IOException {

            if (image != null && !image.isEmpty()) {
                if (imageUrlStr != null) {
                    throw new BusinessException(FACESWAP_EXCLUSIVE_PARAMETERS_PROVIDED);
                }
                return uploadFaceSwapImage(image);
            } else if (isNotEmpty(imageUrlStr)) {
                if(!isImageUrl(imageUrlStr)){
                    throw new BusinessException(IMAGE_URL_ERROR);
                }
                return uploadFaceSwapImage(imageUrlStr);
            }
            return null;

    }

    @Override
    public Object uploadImageBinaryToComfyUI(MultipartFile multipartFile, HttpServletRequest httpServletRequest) throws IOException {

        //获取前端上传的文件名称
        String originalFileName = multipartFile.getOriginalFilename();
        if (originalFileName == null || originalFileName.isEmpty()) {
            return ResultUtil.error(ERROR);
        }
        //取文件名下标，给文件重命名的时候使用
        String suffix = originalFileName.substring(originalFileName.indexOf("."));
        //取一个随机id给文件重命名使用
        String uuid = UUID.randomUUID().toString();
        //你的接收的文件新的名字
        String filename = uuid + suffix;
//        //获取文件在服务器的路径
//        String uri = httpServletRequest.getSession().getServletContext().getRealPath("/");

        /**
         * 这里后面要改成在服务器上的路径
         */
        File file = new File(System.getProperty("user.dir") + "/src/main/resources/static/image/input/" + filename);
        multipartFile.transferTo(file);
        Path path = file.toPath();
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("localFilePath", filename);
        return new JSONObject(resultMap);
    }

    /**
     * 把文件放到multipart/form-data参数中上传
     *
     * @param url
     * @param file
     * @return
     * @throws IOException
     */
    public static Object uploadImageToComfyUI(URL url, File file) throws IOException {
        String boundary = "-------------------------" + stringValue(System.currentTimeMillis());
        String CRLF = "\r\n"; // 换行符
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setDoOutput(true);
        conn.setRequestMethod("POST");
        conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);

        try (
                OutputStream output = conn.getOutputStream();
                PrintWriter writer = new PrintWriter(new OutputStreamWriter(output, "UTF-8"), true);
        ) {
            // 发送文件数据
            String encodedFileName = URLEncoder.encode(file.getName(), "UTF-8").replace("+", "%20");//文件名必须UTF-8编码，不然中文的编码会出现问题
            writer.append("--" + boundary).append(CRLF);
            writer.append("Content-Disposition: form-data; name=\"" + "image" + "\"; filename=\"" + encodedFileName + "\"").append(CRLF);
            writer.append("Content-Type: " + URLConnection.guessContentTypeFromName(encodedFileName)).append(CRLF);
            writer.append(CRLF).flush();

            Files.copy(file.toPath(), output);
            output.flush(); // 确保文件数据发送完毕

            writer.append(CRLF).flush(); // 结束行数据
            writer.append("--" + boundary + "--").append(CRLF);
        }
        StringBuilder response = new StringBuilder();
        BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
        String inputLine;
        while (JObjectUtil.isNotEmpty(inputLine = in.readLine())) {
            response.append(inputLine);
        }
        in.close();

        conn.disconnect();
        return JSON.parse(stringValue(response));
    }
}
